#pragma once

#include <iostream>
#include <string>
#include <ctime>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>

namespace log_ns
{
    enum level
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string LevelTOStr(level infoLevel)
    {
        switch (infoLevel)
        {
        case DEBUG:
            return "DEBUG";
        case INFO:
            return "INFO";
        case WARNING:
            return "WARNING";
        case ERROR:
            return "ERROR";
        case FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }

    std::string GetLocalTime()
    {
        time_t now = time(0);
        struct tm *currTime = localtime(&now);
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",
                 currTime->tm_year + 1900,
                 currTime->tm_mon + 1,
                 currTime->tm_mday,
                 currTime->tm_hour,
                 currTime->tm_min,
                 currTime->tm_sec);
        return buffer;
    }

    class LogMessage
    {
    public:
        std::string _logLevel;
        pid_t _pid;
        std::string _messageInfo;
        std::string _currTime;
        std::string _fileName;
        int _fileNumber;
    };

    class Log
    {
    public:
        void LogFileToScreen(const LogMessage &lg)
        {
            printf("[%s][%d][%s][%s][%d] %s",
                   lg._logLevel.c_str(),
                   lg._pid,
                   lg._currTime.c_str(),
                   lg._fileName.c_str(),
                   lg._fileNumber,
                   lg._messageInfo.c_str());
        }

        void MessageToLog(std::string fileName, int fileNumber, level lv,
                          const char *format,
                          ...)
        {
            LogMessage lg;
            lg._fileName = fileName;
            lg._fileNumber = fileNumber;
            lg._logLevel = LevelTOStr(lv);
            lg._currTime = GetLocalTime();
            lg._pid = getpid();
            char message[2048];
            va_list ap;
            va_start(ap, format);
            vsnprintf(message, sizeof(message), format, ap);
            va_end(ap);
            lg._messageInfo = message;

            LogFileToScreen(lg);
        }
    };

    Log lg;

#define LOG(Level, Format, ...)                                            \
    do                                                                     \
    {                                                                      \
        lg.MessageToLog(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \
    } while (0)
};